/*
 * Copyright (c) 2009, 2012 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Dave Locke - initial API and implementation and/or initial documentation
 */
package org.eclipse.paho.client.mqttv3.util;

import java.util.Enumeration;
import java.util.Properties;

import org.eclipse.paho.client.mqttv3.internal.ClientComms;
import org.eclipse.paho.client.mqttv3.logging.Logger;
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;

/**
 * Utility to help debug problems with the Paho MQTT client Once initialised a call to dumpClientDebug will force any memory trace together with pertinent client and system state
 * to the main log facility.
 *
 * No client wide lock is taken when the dump is progress. This means the set of client state may not be consistent as the client can still be processing work while the dump is in
 * progress.
 */
public class Debug {

    final static String className = ClientComms.class.getName();
    Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className);

    static String separator = "==============";
    static String lineSep = System.getProperty("line.separator", "\n");

    String clientID;
    ClientComms comms;

    /**
     * Set the debug facility up for a specific client
     *
     * @param clientID the ID of the client being debugged
     * @param comms the ClientComms object of the client being debugged
     */
    public Debug(String clientID, ClientComms comms) {
        this.clientID = clientID;
        this.comms = comms;
        log.setResourceName(clientID);
    }

    /**
     * Dump maximum debug info. This includes state specific to a client as well as debug that is JVM wide like trace and system properties. All state is written as debug log
     * entries.
     */
    public void dumpClientDebug() {
        dumpClientComms();
        dumpConOptions();
        dumpClientState();
        dumpBaseDebug();
    }

    /**
     * Dump of JVM wide debug info. This includes trace and system properties. Includes trace and system properties
     */
    public void dumpBaseDebug() {
        dumpVersion();
        dumpSystemProperties();
        dumpMemoryTrace();
    }

    /**
     * If memory trace is being used a request is made to push it to the target handler.
     */
    protected void dumpMemoryTrace() {
        log.dumpTrace();
    }

    /**
     * Dump information that show the version of the MQTT client being used.
     */
    protected void dumpVersion() {
        StringBuffer vInfo = new StringBuffer();
        vInfo.append(lineSep + separator + " Version Info " + separator + lineSep);
        vInfo.append(left("Version", 20, ' ') + ":  " + ClientComms.VERSION + lineSep);
        vInfo.append(left("Build Level", 20, ' ') + ":  " + ClientComms.BUILD_LEVEL + lineSep);
        vInfo.append(separator + separator + separator + lineSep);
        log.fine(className, "dumpVersion", vInfo.toString());
    }

    /**
     * Dump the current set of system.properties to a log record
     */
    public void dumpSystemProperties() {

        Properties sysProps = System.getProperties();
        log.fine(className, "dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString());
    }

    /**
     * Dump interesting variables from ClientState
     */
    public void dumpClientState() {
        Properties props = null;
        if (comms != null && comms.getClientState() != null) {
            props = comms.getClientState().getDebug();
            log.fine(className, "dumpClientState", dumpProperties(props, clientID + " : ClientState").toString());
        }
    }

    /**
     * Dump interesting variables from ClientComms
     */
    public void dumpClientComms() {
        Properties props = null;
        if (comms != null) {
            props = comms.getDebug();
            log.fine(className, "dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString());
        }
    }

    /**
     * Dump Connection options
     */
    public void dumpConOptions() {
        Properties props = null;
        if (comms != null) {
            props = comms.getConOptions().getDebug();
            log.fine(className, "dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString());
        }
    }

    /**
     * Return a set of properties as a formatted string
     */
    public static String dumpProperties(Properties props, String name) {

        StringBuffer propStr = new StringBuffer();
        Enumeration propsE = props.propertyNames();
        propStr.append(lineSep + separator + " " + name + " " + separator + lineSep);
        while (propsE.hasMoreElements()) {
            String key = (String) propsE.nextElement();
            propStr.append(left(key, 28, ' ') + ":  " + props.get(key) + lineSep);
        }
        propStr.append(separator + separator + separator + lineSep);

        return propStr.toString();
    }

    /**
     * Left justify a string.
     *
     * @param s the string to justify
     * @param width the field width to justify within
     * @param fillChar the character to fill with
     *
     * @return the justified string.
     */
    public static String left(String s, int width, char fillChar) {
        if (s.length() >= width) {
            return s;
        }
        StringBuffer sb = new StringBuffer(width);
        sb.append(s);
        for (int i = width - s.length(); --i >= 0;) {
            sb.append(fillChar);
        }
        return sb.toString();
    }

}
